#include <stdint.h>
#include <string.h>
#include "tm16xx.h"
/**
 * @brief 显存地址写入上限
 * @note 依次对应 TM1629D TM1617 TM1638
 */
static const uint8_t s_gramsize[] = {16, 14, 16};
/**
 * @brief 键值地址数目读取上限
 * @note 依次对应 TM1629D TM1617 TM1638
 */
static const uint8_t s_kramsize[] = {4, 5, 4};
/**
 * @brief TM1617显存地址列表
 *
 */
#if TM_USE_MSB
static const uint8_t s_tm1617_addr[] = {0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3};
#else
static const uint8_t s_tm1617_addr[] = {0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD};
#endif
/**
 * @brief TM1629D显存地址列表
 *
 */
#if TM_USE_MSB
static const uint8_t s_tm1629d_addr[] = {0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3};
#else
static const uint8_t s_tm1629d_addr[] = {0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF};
#endif
/**
 * @brief TM1638显存地址列表
 *
 */
#if TM_USE_MSB
static const uint8_t s_tm1638_addr[] = {0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3};
#else
static const uint8_t s_tm1638_addr[] = {0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF};
#endif

static void tm16xx_send_byte(tm16xx_t *p, uint8_t dat);
static uint8_t tm16xx_recv_byte(tm16xx_t *p);
static void tm16xx_set_command(tm16xx_t *p, uint8_t cmd);

/**
 * @brief 默认结构体初始化函数
 *
 * @param p pointer to handle
 * @return int32_t 0 OK
 */
int32_t tm16xx_init_default(tm16xx_t *p)
{
    memset(p, 0, sizeof(tm16xx_t));
    p->chiptype = TM1629D;
    p->displaymode = Mode2Digit8Segment;
    p->brightness = Brightness10_16;
    p->displayonoff = DisplayOn;
    p->addressmode = ModeAddressIncrease; // ModeFixedAddress;//
    p->bitdelay = 2;
    p->ackdelay = 2;
    p->cmddelay = 15;
    memcpy(p->addr, s_tm1629d_addr, sizeof(s_tm1629d_addr));
    for (int i = 0; i < s_gramsize[p->chiptype]; i++)
    {
        p->uaddr[i] = i * 2;
    }
    p->inited = 1;
    return 0;
}
/**
 * @brief 注册 io 驱动函数
 *
 * @param p pointer to handle
 * @return int32_t  0 OK
 */
int32_t tm16xx_register(tm16xx_t *p, tm16xx_io_t *pio)
{
    p->io = *pio;
    p->ioinited = 1;
    return 0;
}

/**
 * @brief 逻辑地址设定函数
 *
 * @param p pointer to handle
 * @return int32_t 0 OK
 */
int32_t tm16xx_set_useraddr(tm16xx_t *p, uint8_t *abuf, uint8_t len)
{
    if (!p->inited)
        return -1;
    if (len > s_gramsize[p->chiptype])
        return -2;
    memcpy(p->uaddr, abuf, len);
    return 0;
}
/**
 * @brief 写显存函数
 *
 * @param p pointer to handle
 * @param dbuf
 * @param len
 * @return int32_t 0 OK
 * -1
 * -2
 * -3
 * -4
 */
int32_t tm16xx_write(tm16xx_t *p, uint8_t *dbuf, uint8_t len)
{
    uint8_t *pd = dbuf;
    uint8_t *pu = p->uaddr;
    if (!p->inited)
        return -1;
    if (!p->ioinited)
        return -2;
    if (p->addressmode == ModeAddressIncrease)
    {
        /**/
        if (((pu[0] - 0xC0) + len) >= s_gramsize[p->chiptype])
            return -3;
        tm16xx_set_command(p, CmdAddressIncrease);
        p->io.dlyus(p->cmddelay);
        p->io.wrste(ChipEnable);
        tm16xx_send_byte(p, p->addr[*pu & 0xF]);
        while (len--)
        {
            tm16xx_send_byte(p, *pd++);
        }
        p->io.wrste(ChipDisable);
        p->io.dlyus(p->cmddelay);
    }
    else
    {
        tm16xx_set_command(p, CmdFixedAddress);
        p->io.dlyus(p->cmddelay);
        while (len--)
        {
            /**/
            if (pu[0] >= s_gramsize[p->chiptype])
                return -4;
            p->io.wrste(ChipEnable);
            tm16xx_send_byte(p, p->addr[(*pu++) & 0xF]);
            tm16xx_send_byte(p, *pd++);
            p->io.wrste(ChipDisable);
            p->io.dlyus(p->cmddelay);
        }
    }
#if TM_USE_MSB
    {
        static const uint8_t sc_list[8] = {0,4,2,6,1,5,3,7};
        tm16xx_set_command(p, CmdDisplay | (p->displayonoff << 4) | (sc_list[p->brightness] << 5));
    }
#else
    tm16xx_set_command(p, CmdDisplay | (p->displayonoff << 3) | p->brightness);
#endif
    return 0;
}
/**
 * @brief 读键值函数
 *
 * @param p pointer to handle
 * @param buf
 * @param len
 * @return int32_t 0 OK
 */
int32_t tm16xx_read(tm16xx_t *p, uint8_t *buf, uint8_t len)
{
    if (!p->inited)
        return -1;
    if (!p->ioinited)
        return -2;
    p->io.wrste(ChipDisable);
    p->io.wrste(ChipEnable);
    tm16xx_send_byte(p, CmdReadKey);
    p->io.dlyus(p->ackdelay);
    for (int i = 0; i < len; i++)
    {
        p->kram[i] = tm16xx_recv_byte(p);
    }
    p->io.wrste(ChipDisable);
    memcpy(buf, p->kram, len);
    return 0;
}
/**
 * @brief 按预设的模式，刷新dlen字节dbuf显示到TM16XX, 并读取klen个字节键值到kbuf
 *
 * @param p pointer to handle
 * @param dbuf 用户显示数据指针
 * @param dlen 用户显示数据字节长度
 * @param kbuf 用户键值保存地址指针
 * @param klen 读取键值字节数
 * @return int32_t 0 OK
 */
int32_t tm16xx_flushall(tm16xx_t *p, uint8_t *dbuf, uint8_t dlen, uint8_t *kbuf, uint8_t klen)
{
    if (!p->inited)
        return -1;
    if (!p->ioinited)
        return -2;
    int32_t ret = 0;
    if (p->chiptype == TM1617)
    {
        switch (p->displaymode)
        {
        case Mode3Digit7Segment:
            tm16xx_set_command(p, Cmd3Digit7Segment);
            break;
        case Mode2Digit8Segment:
            tm16xx_set_command(p, Cmd2Digit8Segment);
            break;
        default:
            break;
        }
    }
    if (dlen == 0)
    {
        ret |= 1;
    }
    else if (dlen <= s_gramsize[p->chiptype])
    {
        if (memcmp(p->gram, dbuf, dlen))
        {
            tm16xx_write(p, dbuf, dlen);
            memcpy(p->gram, dbuf, dlen);
        }
    }
    else
    {
        return -1;
    }
    if (klen == 0)
    {
        ret |= 2;
    }
    else if (klen <= s_kramsize[p->chiptype])
    {
        tm16xx_read(p, kbuf, klen);
    }
    else
    {
        return -2;
    }
    return ret;
}
/**
 * @brief 设定显示辉度
 *
 * @param p pointer to handle
 * @param brightness
 * @return int32_t 0 OK
 */
int32_t tm16xx_set_brightness(tm16xx_t *p, tm16xx_brightness_t brightness)
{
    p->brightness = brightness;
    return 0;
}
/**
 * @brief 设定显示开启/关闭
 *
 * @param p pointer to handle
 * @param displayonoff
 * @return int32_t
 */
int32_t tm16xx_set_displayonoff(tm16xx_t *p, tm16xx_displayonoff_t displayonoff)
{
    p->displayonoff = displayonoff;
    return 0;
}
/**
 * @brief 设定显示模式
 *
 * @param p pointer to handle
 * @param displaymode
 * @return int32_t 0 OK
 */
int32_t tm16xx_set_displaymode(tm16xx_t *p, tm16xx_displaymode_t displaymode)
{
    p->displaymode = displaymode;
    return 0;
}
/**
 * @brief 设定地址增加模式
 *
 * @param p pointer to handle
 * @param addressmode
 * @return int32_t 0 OK
 */
int32_t tm16xx_set_addressmode(tm16xx_t *p, tm16xx_addrssmode_t addressmode)
{
    p->addressmode = addressmode;
    return 0;
}
/**
 * @brief 设定驱动芯片类型
 *
 * @param p pointer to handle
 * @param chiptype
 * @return int32_t 0 OK
 */
int32_t tm16xx_set_chiptype(tm16xx_t *p, tm16xx_chiptype_t chiptype)
{
    int32_t ret = 0;
    p->chiptype = chiptype;
    switch (p->chiptype)
    {
    case TM1617:
        memcpy(p->addr, s_tm1617_addr, sizeof(s_tm1617_addr));
        break;
    case TM1629D:
        memcpy(p->addr, s_tm1629d_addr, sizeof(s_tm1629d_addr));
        break;
    case TM1638:
        memcpy(p->addr, s_tm1638_addr, sizeof(s_tm1638_addr));
        break;
    default:
        ret = -1;
        break;
    }
    return ret;
}
enum seg_enum
{
#if TM_USE_MSB
    dd_0 = 0xfc,
    dd_1 = 0x60,
    dd_2 = 0xda,
    dd_3 = 0xf2,
    dd_4 = 0x66,
    dd_5 = 0xb6,
    dd_6 = 0xbe,
    dd_7 = 0xe0,
    dd_8 = 0xfe,
    dd_9 = 0xf6,
    dd_A = 0xee,
    dd_a = 0xee,
    dd_B = 0x3e,
    dd_b = 0x3e,
    dd_C = 0x9c,
    dd_c = 0x1a,
    dd_D = 0x7a,
    dd_d = 0x7a,
    dd_E = 0x9e,
    dd_e = 0xde,
    dd_F = 0x8e,
    dd_f = 0x8e,
    dd_G = 0xbc,
    dd_g = 0xbc,
    dd_H = 0x6e,
    dd_h = 0x2e,
    dd_I = 0xF0,
    dd_i = 0xF0,
    dd_J = 0x70,
    dd_j = 0x70,
    dd_K = 0xae,
    dd_k = 0xae,
    dd_L = 0x1c,
    dd_l = 0x0c,
    dd_M = 0xec,
    dd_m = 0xec,
    dd_N = 0x2a,
    dd_n = 0x2a,
    dd_O = 0x3a,
    dd_o = 0x3a,
    dd_P = 0xce,
    dd_p = 0xce,
    dd_Q = 0xe6,
    dd_q = 0xe6,
    dd_R = 0x4e,
    dd_r = 0x4e,
    dd_S = 0x92,
    dd_s = 0x92,
    dd_T = 0x8c,
    dd_t = 0x1e,
    dd_U = 0x7c,
    dd_u = 0x7c,
    dd_V = 0x38,
    dd_v = 0x38,
    dd_W = 0x7E,
    dd_w = 0x7E,
    dd_X = 0x26,
    dd_x = 0x26,
    dd_Y = 0x76,
    dd_y = 0x76,
    dd_Z = 0x5a,
    dd_z = 0x5a,
    dd__ = 0x02,
    dd_off = 0x00,
    dd_dot = 0x01,
    dd_no = 0,
    dd_00 = 0x28,
    dd_01 = 0x68,
    dd_10 = 0x2c,
    dd_11 = 0x6c
#else
    dd_0 = 0x3f,
    dd_1 = 0x06,
    dd_2 = 0x5b,
    dd_3 = 0x4f,
    dd_4 = 0x66,
    dd_5 = 0x6d,
    dd_6 = 0x7d,
    dd_7 = 0x07,
    dd_8 = 0x7f,
    dd_9 = 0x6f,
    dd_A = 0x77,
    dd_a = 0x77,
    dd_B = 0x7c,
    dd_b = 0x7c,
    dd_C = 0x39,
    dd_c = 0x58,
    dd_D = 0x5E,
    dd_d = 0x5E,
    dd_E = 0x79,
    dd_e = 0x7b,
    dd_F = 0x71,
    dd_f = 0x71,
    dd_G = 0x3d,
    dd_g = 0x3d,
    dd_H = 0x76,
    dd_h = 0x74,
    dd_I = 0x0F,
    dd_i = 0x0F,
    dd_J = 0x0e,
    dd_j = 0x0e,
    dd_K = 0x75,
    dd_k = 0x75,
    dd_L = 0x38,
    dd_l = 0x30,
    dd_M = 0x37,
    dd_m = 0x37,
    dd_N = 0x54,
    dd_n = 0x54,
    dd_O = 0x5c,
    dd_o = 0x5c,
    dd_P = 0x73,
    dd_p = 0x73,
    dd_Q = 0x67,
    dd_q = 0x67,
    dd_R = 0x72,
    dd_r = 0x72,
    dd_S = 0x49,
    dd_s = 0x49,
    dd_T = 0x31,
    dd_t = 0x78,
    dd_U = 0x3E,
    dd_u = 0x3E,
    dd_V = 0x1c,
    dd_v = 0x1c,
    dd_W = 0x7E,
    dd_w = 0x7E,
    dd_X = 0x64,
    dd_x = 0x64,
    dd_Y = 0x6E,
    dd_y = 0x6E,
    dd_Z = 0x5A,
    dd_z = 0x5A,
    dd__ = 0x40,
    dd_off = 0x00,
    dd_dot = 0x80,
    dd_no = 0,
    dd_00 = 0x14,
    dd_01 = 0x16,
    dd_10 = 0x34,
    dd_11 = 0x36
#endif
};

const uint8_t ascii_table[128] =
    {
        dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__,
        dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__,
        0, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd__, dd_dot, dd__,
        dd_0, dd_1, dd_2, dd_3, dd_4, dd_5, dd_6, dd_7, dd_8, dd_9, dd__, dd__, dd__, dd__, dd__, dd__,
        dd__, dd_A, dd_B, dd_C, dd_D, dd_E, dd_F, dd_G, dd_H, dd_I, dd_J, dd_K, dd_L, dd_M, dd_N, dd_O,
        dd_P, dd_Q, dd_R, dd_S, dd_T, dd_U, dd_V, dd_W, dd_X, dd_Y, dd_Z, dd__, dd__, dd__, dd__, dd__,
        dd_X, dd_a, dd_b, dd_c, dd_d, dd_e, dd_f, dd_g, dd_h, dd_i, dd_j, dd_k, dd_l, dd_m, dd_n, dd_o,
        dd_p, dd_q, dd_r, dd_s, dd_t, dd_u, dd_v, dd_w, dd_x, dd_y, dd_z, dd__, dd__, dd__, dd__, dd__};

#define min(a,b) (((a)<=(b)) ? (a) : (b))
int32_t display_format_string(char *c, uint8_t *buf, uint8_t len)
{
    uint8_t *p = (uint8_t *)c;
    len = min(len, strlen(c));
    for (int i = 0; i < len; i++)
        buf[i] = ascii_table[p[i] & 0x7F];
    return len;
}

int32_t display_format_hex(uint32_t v, uint8_t *buf, uint8_t len)
{
    int32_t cv, b, a, ret = 0;

    cv = v;
    while (len--)
    {
        a = cv & 0xF;
        b = (a > 9) ? (0x41 + a - 10) : (0x30 + a);
        buf[len] = ascii_table[b];
        cv >>= 4;
        ret++;
    }
    return ret;
}

int32_t display_format_dec(int32_t v, uint8_t *buf, uint8_t len)
{
    uint8_t sign = 0;
    int32_t a = v, ba, ret = 0;
    if (a < 0)
    {
        a = -a;
        sign = 1;
    }
    while (len--)
    {
        ba = a % 10;
        buf[len] = ascii_table['0' + ba];
        if (sign && (0 == ret))
            buf[len] |= dd_dot;
        a /= 10;
        ret++;
    }
    return ret;
}

static void tm16xx_send_byte(tm16xx_t *p, uint8_t dat)
{
    p->io.wrsck(1);
    for (uint8_t i = 0; i < 8; i++)
    {
        p->io.wrsck(0);
#if TM_USE_MSB
        if (dat & 0x80)
        {
            p->io.wrdio(1);
        }
        else
        {
            p->io.wrdio(0);
        }
        dat = dat << 1;
#else
        if (dat & 0x01)
        {
            p->io.wrdio(1);
        }
        else
        {
            p->io.wrdio(0);
        }
        dat = dat >> 1;
#endif
        p->io.dlyus(p->bitdelay);
        p->io.wrsck(1);
        p->io.dlyus(p->bitdelay);
    }
    /*释放数据线*/
    p->io.wrdio(1);
}
static uint8_t tm16xx_recv_byte(tm16xx_t *p)
{
    uint8_t dat = 0;
    p->io.wrsck(1);
    for (uint8_t i = 0; i < 8; i++)
    {
#if TM_USE_MSB
        dat <<= 1;
        p->io.dlyus(p->bitdelay);
        p->io.wrsck(0);
        p->io.dlyus(p->bitdelay);
        if (p->io.rddio())
            dat |= 0x01;
#else
        dat >>= 1;
        p->io.dlyus(p->bitdelay);
        p->io.wrsck(0);
        p->io.dlyus(p->bitdelay);
        if (p->io.rddio())
            dat |= 0x80;
#endif
        p->io.wrsck(1);
    }
    return dat;
}

/**
 * @brief 向TM16XX发送命令
 *
 * @param cmd input @tm16xx_command_t
 */
static void tm16xx_set_command(tm16xx_t *p, uint8_t cmd)
{
    p->io.wrste(ChipEnable);
    tm16xx_send_byte(p, cmd);
    p->io.wrste(ChipDisable);
}
